package cn.xhh.dailyreview.server.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import cn.xhh.dailyreview.server.model.Category;
import cn.xhh.dailyreview.server.model.Review;
import cn.xhh.dailyreview.server.model.User;
import cn.xhh.dailyreview.server.utils.DbUtil;

/**
 * Data Access Object for {@link User}
 * @author cooky
 */
public class UserDAO {
	
	private static final Logger log = Logger.getLogger(UserDAO.class.getName());
	
	/**
	 * @return <code>true</code>: operation succeeded; <code>false</code>: failed.
	 */
	public boolean createOrUpdate(User user) {
		if (user == null || user.invalid()) {
			log.severe("Creating/updating user failed - invalid: " + user);
			return false;
		}

		if (log.isLoggable(Level.FINEST))
			log.finest("Creating/updating user...");
		
		boolean result = true;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			pm.makePersistent(user);
		} finally {
			pm.close();
		}
		
		if (result && log.isLoggable(Level.FINE))
			log.fine("User created/updated. ID: " + user.getId() + ". Data: " + user);
		return result;
	}

	/**
	 * @return the user found, <code>null</code> for not found.
	 */
	public User read(Long id) {
		if (id == null) {
			log.severe("Reading user failed: ID is null.");
			return null;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading user with ID: " + id);
		
		User user = null;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			user = pm.getObjectById(User.class, id);
		} catch (JDOObjectNotFoundException e) {
			user = null;
			log.warning("Reading user failed: no user found with ID: " + id);
		} finally {
			pm.close();
		}
		
		if (user!=null && log.isLoggable(Level.FINER))
			log.finer("User read. ID: " + user.getId() + ". Data: [ " + user + " ]");
		return user;
	}
	
	public List<User> read(String email) {
		return read(email, null);
	}
	
	/**
	 * @return users found, <code>null</code> for error.
	 */
	@SuppressWarnings("unchecked")
	public List<User> read(String email, String password) {
		if (email == null) {
			log.severe("Reading user failed: null email.");
			return null;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading user with email: " + email);
		
		List<User> users = null;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			Query q = pm.newQuery(User.class);
			List<User> found;
			if (password == null) {
				q.setFilter("email == pEmail");
				q.declareParameters("String pEmail");
				found = (List<User>) q.execute(email);
			} else {
				q.setFilter("email == pEmail && password == pPasswd");
				q.declareParameters("String pEmail, String pPasswd");
				found = (List<User>) q.execute(email, password);
			}
			if (found != null) {
				users = new ArrayList<User>(found.size());
				users.addAll(found);
			}
			q.closeAll();
		} finally {
			pm.close();
		}
		
		if (log.isLoggable(Level.FINER))
			log.finer("Users read with email: " + email + ". Count: "
					+ (users==null ? null : users.size()));
		return users;
	}

	/**
	 * Query users
	 */
	@SuppressWarnings("unchecked")
	public List<User> read(int base, int max) {
		if (base<0 || max<0) {
			log.severe("Reading users failed: illegal arguments (base: " 
					+ base + ", max: " + max + ")");
			return null;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading users... base: " + base + ", max: " + max);
		
		List<User> users = null;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			Query q = pm.newQuery(User.class);
			q.setOrdering("creationTime asc, id asc");
			q.setRange(base, max==0 ? Long.MAX_VALUE : base+max);
			List<User> got = (List<User>) q.execute();
			users = new ArrayList<User>(got.size());
			users.addAll(got);
			q.closeAll();
		} finally {
			pm.close();
		}
		
		if (log.isLoggable(Level.FINER))
			log.finer("Users read: " + (users==null ? null : users.size()) 
					+ ". (base: " + base + ", max: " + max + ")");
		return users;
	}
	
	public List<User> readAll() {
		return read(0, 0);
	}
	
	public int readCount() {
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading users count...");
		
		int count = -1;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			Query q = pm.newQuery(User.class);
			q.setResult("count(this)");
			q.setUnique(true);
			count = (Integer) q.execute();
			q.closeAll();
		} finally {
			pm.close();
		}
		
		if (log.isLoggable(Level.FINER))
			log.finer("Users count read: " + count);
		return count;
	}
	
	/**
	 * @return <code>true</code>: operation succeeded; <code>false</code>: failed.
	 */
	public boolean delete(User user) {
		if (user==null || user.getId()==null) {
			log.severe("Deleting user failed: " + (user==null ? "user" : "ID") + " is null.");
			return false;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Deleting user... ID: " + user.getId());
		
		boolean result = true;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			// first make sure the user is persistent and existent
			user = pm.getObjectById(User.class, user.getId());
//			Transaction tx = pm.currentTransaction(); // XXX change the model classes to implement Entity Group from JDO: User, Category, Review.
//			try {
//				tx.begin();
				pm.newQuery(Review.class, "userId == " + user.getId()).deletePersistentAll();
				pm.newQuery(Category.class, "userId == " + user.getId()).deletePersistentAll();
				pm.deletePersistent(user);
//				tx.commit();
//			} finally {
//				if (tx.isActive()) {
//					tx.rollback();
//					l.severe("Deleting user failed: rollback. ID: " + user.getId());
//					result = false;
//				}
//			}
		} catch (JDOObjectNotFoundException e) {
			log.warning("Deleting user failed: no user found with ID: " + user.getId());
			result = false;
		} finally {
			pm.close();
		}
		
		if (result && log.isLoggable(Level.FINE))
			log.fine("User deleted: " + user.getId());
		return result;
	}

}
